# -*- coding: utf-8 -*-
"""
人物选择改造
By Logic & AnpyD
"""

import bs
import bsLobby
import bsUtils
import bsSpaz
import random
import weakref
import json
import os
import bsUI
import bsInternal
from bsLobby import *
from game_stats import DATA_PATH
import account_system

# 重载UTF8
import sys
reload(sys)
sys.setdefaultencoding('utf-8')


gRandProfileIndex = 1
gLastWarnTime = 0
gRandomCharIndexOffset = None
change_limit = 15
ChangeMessage = bsLobby.ChangeMessage

def get_account_inf(player, _type):

    # 获取玩家ID
    accountID = player.get_account_id()
    account_information_data = account_system.get_data()

    # 读取统计文件
    FILE = open(DATA_PATH + "/player_stats.json")
    DATA = json.loads(FILE.read())
    
    # 获取玩家信息
    if accountID not in DATA or accountID not in account_information_data:
        information = "未检测到此账户在服务器的注册信息！\n请在服务器完成 2 场比赛重试吧！"

    else:
        if _type == "games":
            deaths = DATA[accountID]["deaths"]
            games = DATA[accountID]["games"]
            kills = DATA[accountID]["kills"]
            rank = DATA[accountID]["rank"]
            scores = DATA[accountID]["scores"]
            name = player.getName()

            information = "玩家名称: " + str(name) + " | 玩家排名: " + str(rank) + " | 累计得分: " + str(scores) + "\n总击杀数: " + str(kills) + " | 游玩次数: " + str(games) + " | 死亡数: " + str(deaths)

        elif _type == "account":
            name = player.getName()
            tickets = account_information_data[accountID]["tickets"]
            rank = DATA[accountID]["rank"]
            information = "玩家名称: " + str(name) + " | 现有资产: " + str(tickets) + " | 玩家等级: "+ str(account_system.get_level(accountID))

        elif _type == "buff":

            if account_information_data[accountID]["buffs"] != {}:
                buffs = account_information_data[accountID]["buffs"]
            else:
                buffs = None
            if buffs == None:
                information = "这里什么也没有..."
            else:
                name = player.getName()
                _text = name + "的人物Buff\n"
                for i in buffs.keys():
                    _text += str(i) + " : " + str(account_information_data[accountID]["buffs"][i]) + "\n"
                information = _text

        elif _type == "effect":
            if account_information_data[accountID]["effects"] != {}:
                effects = account_information_data[accountID]["effects"]
            else:
                effects = None
            
            if effects == None:
                information = "这里什么也没有..."
            else:
                name = player.getName()
                _text = name + "的人物特效\n"
                for i in effects.keys():
                    _text += str(i) + " : " + str(account_information_data[accountID]["effects"][i]) + "\n"
                information = _text

    return information



def _setReady(self, ready):

    import bsInternal

    profileName = self.profileNames[self.profileIndex]

    # handle '_edit' as a special case
    if profileName == '_edit' and ready:
        import bsUI
        with bs.Context('UI'):
            bsUI.PlayerProfilesWindow(inMainMenu=False)
            # give their input-device UI ownership too
            # (prevent someone else from snatching it in crowded games)
            bsInternal._setUIInputDevice(self._player.getInputDevice())
        return

    if ready == False:
        self._player.assignInputCall(
            'leftPress', bs.Call(self.handleMessage, ChangeMessage('team',
                                                                   -1)))
        self._player.assignInputCall(
            'rightPress', bs.Call(self.handleMessage, ChangeMessage('team',
                                                                    1)))
        self._player.assignInputCall(
            'bombPress',
            bs.Call(self.handleMessage, ChangeMessage('character', 1)))
        self._player.assignInputCall(
            'upPress',
            bs.Call(self.handleMessage, ChangeMessage('profileIndex', -1)))
        self._player.assignInputCall(
            'downPress',
            bs.Call(self.handleMessage, ChangeMessage('profileIndex', 1)))
        self._player.assignInputCall(
            ('jumpPress', 'pickUpPress', 'punchPress'),
            bs.Call(self.handleMessage, ChangeMessage('ready', 1)))
        self.ready = False
        self._updateText()
        self._player.setName('untitled', real=False)
    elif ready == True:
        self._player.assignInputCall(
            ('leftPress', 'rightPress', 'upPress', 'downPress', 'jumpPress',
             'bombPress', 'pickUpPress'), self._doNothing)
        self._player.assignInputCall(
            'upPress', bs.Call(self.handleMessage, ChangeMessage('screen',
                                                                 -1)))
        self._player.assignInputCall(
            'downPress', bs.Call(self.handleMessage,
                                 ChangeMessage('screen', 1)))
        self._player.assignInputCall(
            'jumpPress', bs.Call(self.handleMessage, ChangeMessage('ready',
                                                                   0)))
        self._player.assignInputCall(
            ('bombPress', 'pickUpPress', 'punchPress'),
            bs.Call(self.handleMessage, ChangeMessage('ready', 2)))
        # store the last profile picked by this input for reuse
        inputDevice = self._player.getInputDevice()
        name = inputDevice.getName()
        uniqueID = inputDevice.getUniqueIdentifier()
        try:
            deviceProfiles = bs.getConfig()['Default Player Profiles']
        except Exception:
            deviceProfiles = bs.getConfig()['Default Player Profiles'] = {}

        # make an exception if we have no custom profiles and are set
        # to random; in that case we'll want to start picking up custom
        # profiles if/when one is made so keep our setting cleared
        haveCustomProfiles = (True if [
            p for p in self.profiles
            if p not in ('_random', '_edit', '__account__')
        ] else False)
        if profileName == '_random' and not haveCustomProfiles:
            try:
                del (deviceProfiles[name + ' ' + uniqueID])
            except Exception:
                pass
        else:
            deviceProfiles[name + ' ' + uniqueID] = profileName
        bs.writeConfig()
        self._player.setName(self._getName(full=True, clamp=True),
                             self._getName(full=True, clamp=False),
                             real=True)
        self.ready = True
        self._updateText()
    else:
        if self.screen == 'join':
            if self._player.get_account_id() == None:
                bs.screenMessage("抱歉, 服务器暂时无法获取你的账户信息！\n 请尝试稍后加入服务器...", 
                                clients = [self._player.getInputDevice().getClientID()], 
                                transient=True, 
                                color=(1, 0, 0))
                bs.Call(self.handleMessage, ChangeMessage('ready',0))
            else:
                account_system.register(account_id=self._player.getInputDevice().getClientID())
                bs.getSession().handleMessage(PlayerReadyMessage(self))

        elif self.screen == 'game_info':
            try:
                bs.screenMessage(get_account_inf(self._player, "games"), 
                                clients = [self._player.getInputDevice().getClientID()], 
                                transient=True, 
                                color=(0, 1.3, 0))
                bs.screenMessage('提示: 账户信息仅本人可见！', 
                                clients = [self._player.getInputDevice().getClientID()], 
                                transient=True, 
                                color=(0, 0, 0.9))
            except:
                bs.screenMessage('目前无法获取你的账户信息！请至少完成一场比赛再试！', 
                                clients = [self._player.getInputDevice().getClientID()], 
                                transient=True, 
                                color=(1, 0, 0))
            self.screen = 'join'
            self._updateText()

        elif self.screen == 'account_info':
            try:
                bs.screenMessage(get_account_inf(self._player, "account"), 
                                clients = [self._player.getInputDevice().getClientID()], 
                                transient=True, 
                                color=(0, 1.3, 0))
                bs.screenMessage('提示: 账户信息仅本人可见！', 
                                clients = [self._player.getInputDevice().getClientID()], 
                                transient=True, 
                                color=(0, 0, 0.9))
            except:
                bs.screenMessage('目前无法获取你的账户信息！请至少完成一场比赛再试！', 
                                clients = [self._player.getInputDevice().getClientID()], 
                                transient=True, 
                                color=(1, 0, 0))
            self.screen = 'join'
            self._updateText()

        elif self.screen == 'buff_info':
            try:
                bs.screenMessage(get_account_inf(self._player, "buff"), 
                                clients = [self._player.getInputDevice().getClientID()], 
                                transient=True, 
                                color=(0, 1.3, 0))
                bs.screenMessage('提示: 账户信息仅本人可见！', 
                                clients = [self._player.getInputDevice().getClientID()], 
                                transient=True, 
                                color=(0, 0, 0.9))
            except:
                bs.screenMessage('目前无法获取你的账户信息！请稍后再试！', 
                                clients = [self._player.getInputDevice().getClientID()], 
                                transient=True, 
                                color=(1, 0, 0))
            self.screen = 'join'
            self._updateText()

        elif self.screen == 'effect_info':
            try:
                bs.screenMessage(get_account_inf(self._player, "effect"), 
                                clients = [self._player.getInputDevice().getClientID()], 
                                transient=True, 
                                color=(0, 1.3, 0))
                bs.screenMessage('提示: 账户信息仅本人可见！', 
                                clients = [self._player.getInputDevice().getClientID()], 
                                transient=True, 
                                color=(0.0, 0.0, 0.9))
            except:
                bs.screenMessage('目前无法获取你的账户信息！请稍后再试！', 
                                clients = [self._player.getInputDevice().getClientID()], 
                                transient=True, 
                                color=(1, 0, 0))
            self.screen = 'join'
            self._updateText()

        elif self.screen == 'server_info':
            bs.screenMessage('服务器制作 - [AnpyD & China007]; 运行环境: Debian Buster \n 核心版本:3.2 @1.4.155; 最终更新时间:21/8/25', 
                            clients = [self._player.getInputDevice().getClientID()], 
                            transient=True, 
                            color=(0, 1.3, 0))            
            self.screen = 'join'
            self._updateText()

       


def handleMessage(self, msg):

    if isinstance(msg, ChangeMessage):

        # if we've been removed from the lobby, ignore this stuff
        if msg.what == 'team' and not self._admin:
            self.change_count += 1
            if self.change_count > change_limit -5:
                bs.screenMessage(
                    '切换人物超频! 警告次数: {}/5'.format(
                        self.change_count-(change_limit-5)),
                    clients=[self._player.getInputDevice().getClientID()],
                    color=(1, 0.2, 0.2),
                    transient=True)
            if self.change_count >= change_limit:
                bs.screenMessage('切换人物超频次！已被系统退出！',
                                 color=(1, 0, 0),
                                 transient=True)
                bsInternal._disconnectClient(
                    self._player.getInputDevice().getClientID())
        if self._dead:
            print ("WARNING: chooser got ChangeMessage after dying")
            return

        if not self._textNode.exists():
            bs.printError('got ChangeMessage after nodes died')
            return
        if msg.what == 'screen':
            self.screen = self.screens[(
                (self.screens.index(self.screen) + msg.value) %
                len(self.screens))]
            self._updateText()
        if msg.what == 'team':
            if len(self.getLobby()._teams) > 1:
                bs.playSound(self._swishSound)
            self._selectedTeamIndex = ((self._selectedTeamIndex + msg.value) %
                                       len(self.getLobby()._teams))
            self._updateText()
            self.updatePosition()
            self._updateIcon()

        elif msg.what == 'profileIndex':
            if len(self.profileNames) == 1:
                # this should be pretty hard to hit now with
                # automatic local accounts..
                bs.playSound(bs.getSound('error'))
            else:
                # pick the next player profile and assign our name
                # and character based on that
                bs.playSound(self._deekSound)
                self.profileIndex = ((self.profileIndex + msg.value) %
                                     len(self.profileNames))
                self.updateFromPlayerProfile()

        elif msg.what == 'character':
            bs.playSound(self._clickSound)
            # update our index in our local list of characters
            self.characterIndex = ((self.characterIndex + msg.value) %
                                   len(self.characterNames))
            # bs.screenMessage(self.characterNames[self.characterIndex])
            self._updateText()
            self._updateIcon()

        elif msg.what == 'ready':
            forceTeamSwitch = False
            # team auto-balance kicks us to another team if we try to
            # join the team with the most players
            if not self.ready:
                if bs.getConfig().get('Auto Balance Teams', False):
                    lobby = self.getLobby()
                    if len(lobby._teams) > 1:
                        session = bs.getSession()
                        # first, calc how many players are on each team
                        # ..we need to count both active players and
                        # choosers that have been marked as ready.
                        teamPlayerCounts = {}
                        for team in lobby._teams:
                            teamPlayerCounts[team().getID()] = \
                                len(team().players)
                        for chooser in lobby.choosers:
                            if chooser.ready:
                                teamPlayerCounts[
                                    chooser.getTeam().getID()] += 1
                        largestTeamSize = max(teamPlayerCounts.values())
                        smallestTeamSize = \
                            min(teamPlayerCounts.values())
                        # force switch if we're on the biggest team
                        # and there's a smaller one available
                        if (largestTeamSize != smallestTeamSize
                                and teamPlayerCounts[self.getTeam().getID()] >=
                                largestTeamSize):
                            forceTeamSwitch = True

            if forceTeamSwitch:
                bs.playSound(self._errorSound)
                self.handleMessage(ChangeMessage('team', 1))
                bs.screenMessage(
                    "为了保持队伍人数平衡，请加入另一支队伍！",
                    color=(1, 0, 0),
                    clients=[self._player.getInputDevice().getClientID()],
                    transient=True)
            else:
                if msg.value != 2:
                    bs.playSound(self._punchSound)
                self._setReady(msg.value)


def _updateText(self):

    if self.ready:
        # once we're ready, we've saved the name, so lets ask the system
        # for it so we get appended numbers and stuff
        text = bs.Lstr(value=self._player.getName(full=True))
        if self.screen == 'join':
            text = bs.Lstr(value='${A} ${B}',
                           subs=[('${A}', text),
                                 ('${B}', bs.Lstr(value=u'> 加入游戏🚪 '))])
        elif self.screen == 'account_info':
            text = bs.Lstr(value='${A} ${B}',
                           subs=[('${A}', text),
                                 ('${B}', bs.Lstr(value=u'> 账户信息📜 '))])
        elif self.screen == 'game_info':
            text = bs.Lstr(value='${A} ${B}',
                           subs=[('${A}', text),
                                 ('${B}', bs.Lstr(value=u'> 游戏信息🎮 '))])
        elif self.screen == 'effect_info':
            text = bs.Lstr(value='${A} ${B}',
                           subs=[('${A}', text),
                                 ('${B}', bs.Lstr(value=u'> 人物特效🎉 '))])
        elif self.screen == 'buff_info':
            text = bs.Lstr(value='${A} ${B}',
                           subs=[('${A}', text),
                                 ('${B}', bs.Lstr(value=u'> 人物Buff🛡 '))])
        elif self.screen == 'server_info':
            text = bs.Lstr(value='${A} ${B}',
                           subs=[('${A}', text),
                                 ('${B}', bs.Lstr(value=u'> 关于服务器⚙️ '))])
    else:
        text = bs.Lstr(value=self._getName(full=True))

    canSwitchTeams = len(self.getLobby()._teams) > 1

    # flash as we're coming in
    finColor = bs.getSafeColor(self.getColor()) + (1, )
    if not self._inited:
        bsUtils.animateArray(self._textNode, 'color', 4, {
            150: finColor,
            250: (2, 2, 2, 1),
            350: finColor
        })
    else:
        # blend if we're in teams mode; switch instantly otherwise
        if canSwitchTeams:
            bsUtils.animateArray(self._textNode, 'color', 4, {
                0: self._textNode.color,
                100: finColor
            })
        else:
            self._textNode.color = finColor

    self._textNode.text = text


def __init__(self, vPos, player, lobby):

    import bsInternal

    self._deekSound = bs.getSound('deek')
    self._clickSound = bs.getSound('click01')
    self._punchSound = bs.getSound('punch01')
    self._swishSound = bs.getSound('punchSwish')
    self._errorSound = bs.getSound('error')
    self._maskTexture = bs.getTexture('characterIconMask')
    self._vPos = vPos
    self._lobby = weakref.ref(lobby)
    self._player = player
    self.change_count = 0
    self._inited = False
    self._dead = False
    self._admin = False

    # load available profiles either from the local config or from the
    # remote device..
    self.reloadProfiles()

    # note: this is just our local index out of available teams; *not*
    # the team ID!
    self._selectedTeamIndex = self.getLobby().nextAddTeam

    # store a persistent random character index; we'll use this for the
    # '_random' profile. Let's use their inputDevice id to seed it.. this
    # will give a persistent character for them between games and will
    # distribute characters nicely if everyone is random
    try:
        inputDeviceID = self._player.getInputDevice().getID()
    except Exception as e:
        print('ERROR: exc getting inputDeviceID for chooser creation:', e)
        inputDeviceID = 0
        import traceback
        traceback.print_stack()

    # we want the first device that asks for a chooser to always get
    # spaz as a random character..
    global gRandomCharIndexOffset
    if gRandomCharIndexOffset is None:
        # scratch that.. we now kinda accomplish the same thing with
        # account profiles so lets just be fully random here..
        gRandomCharIndexOffset = random.randrange(1000)

    # to calc our random index we pick a random character out of our
    # unlocked list and then locate that character's index in the full list

    self._randomCharacterIndex = ((inputDeviceID + gRandomCharIndexOffset) %
                                  len(self.characterNames))

    self._randomColor, self._randomHighlight = \
        bsUtils.getPlayerProfileColors(None)
    global gAccountProfileDeviceID
    # attempt to pick an initial profile based on what's been stored
    # for this input device
    try:
        inputDevice = self._player.getInputDevice()
        name = inputDevice.getName()
        uniqueID = inputDevice.getUniqueIdentifier()
        self.profileName = (
            bs.getConfig()['Default Player Profiles'][name + ' ' + uniqueID])
        self.profileIndex = self.profileNames.index(self.profileName)

        # if this one is __account__ and is local and we havn't marked
        # anyone as the account-profile device yet, mark this guy as it.
        # (prevents the next joiner from getting the account profile too)
        if (self.profileName == '__account__'
                and not inputDevice.isRemoteClient()
                and gAccountProfileDeviceID is None):
            gAccountProfileDeviceID = inputDeviceID

    # well hmm that didn't work.. pick __account__, _random, or some
    # other random profile..
    except Exception:

        profileNames = self.profileNames

        # we want the first local input-device in the game to latch on to
        # the account profile
        if (not inputDevice.isRemoteClient()
                and not inputDevice.isControllerApp()):
            if (gAccountProfileDeviceID is None
                    and '__account__' in profileNames):
                gAccountProfileDeviceID = inputDeviceID

        # if this is the designated account-profile-device, try to default
        # to the account profile
        if (inputDeviceID == gAccountProfileDeviceID
                and '__account__' in profileNames):
            self.profileIndex = profileNames.index('__account__')
        else:
            # if this is the controller app, it defaults to using a random
            # profile (since we can pull the random name from the app)
            if inputDevice.isControllerApp():
                self.profileIndex = profileNames.index('_random')
            else:
                # if its a client connection, for now just force the account
                # profile if possible.. (need to provide a way for clients
                # to specify/remember their default profile)
                if (inputDevice.isRemoteClient()
                        and '__account__' in profileNames):
                    self.profileIndex = profileNames.index('__account__')
                else:
                    global gRandProfileIndex
                    # cycle through our non-random profiles once; after
                    # that, everyone gets random.
                    while (gRandProfileIndex < len(profileNames)
                           and profileNames[gRandProfileIndex]
                           in ('_random', '__account__', '_edit')):
                        gRandProfileIndex += 1
                    if gRandProfileIndex < len(profileNames):
                        self.profileIndex = gRandProfileIndex
                        gRandProfileIndex += 1
                    else:
                        self.profileIndex = profileNames.index('_random')

        self.profileName = profileNames[self.profileIndex]

    self.characterIndex = self._randomCharacterIndex
    self._color = self._randomColor
    self._highlight = self._randomHighlight
    self.ready = False
    self.screen = 'join'
    self.screens = ['join', 'account_info', 'game_info', 'effect_info', 'buff_info', 'server_info']
    self._textNode = bs.newNode('text',
                                delegate=self,
                                attrs={
                                    'position': (-100, self._vPos),
                                    'maxWidth': 160,
                                    'shadow': 0.5,
                                    'vrDepth': -20,
                                    'hAlign': 'left',
                                    'vAlign': 'center',
                                    'vAttach': 'top'
                                })

    bsUtils.animate(self._textNode, 'scale', {0: 0, 300: 1.0})
    bs.animate(self._textNode, 'rotate', {0: 0.0, 300: 360.0}, loop=False)
    self.icon = bs.newNode('image',
                           owner=self._textNode,
                           attrs={
                               'position': (-130, self._vPos + 20),
                               'maskTexture': self._maskTexture,
                               'vrDepth': -10,
                               'attach': 'topCenter'
                           })

    bsUtils.animateArray(self.icon, 'scale', 2, {0: (0, 0), 500: (45, 45)})
    bs.animate(self.icon, 'rotate', {0: 0.0, 500: 360.0}, loop=False)

    self._setReady(False)

    # set our initial name to '<choosing player>' in case anyone asks..
    self._player.setName(bs.Lstr(resource='choosingPlayerText').evaluate(),
                         real=False)

    self.updateFromPlayerProfile()
    try:
        self.loadAllCharacters()
    except:
        pass
    self.updatePosition()
    self._inited = True


def loadAllCharacters(self):
    account_id = self._player.get_account_id()
    if account_id is None:
        return
    items = []
    if items is not None:
        if isinstance(getattr(self, "characterNames", None), list):
            try:
                if 'bschars' in items:
                    # for name,profile in self.profiles.items():
                    #     character = profile.get("character","Spaz")
                    #     if (character not in self.characterNames
                    #             and character in bsSpaz.appearances):
                    #         self.characterNames.append(character)
                    for i, k in bsSpaz.appearances.items():
                        if k.style != 'spaz' and i not in self.characterNames:
                            self.characterNames.append(i)
                    bs.screenMessage(
                        'Bombsquad Characters Unlocked',
                        color=(1, 1, 1),
                        clients=[self._player.getInputDevice().getClientID()],
                        transient=True)
                elif 'modchars' in items:
                    self.characterNames.append('PowerRanger')
                    self.characterNames.append('UltraWarrior')
                    self.characterNames.append('RockStar')
                    self.characterNames.append('DestroyerMachine')
                    self.characterNames.append('Masker')
                    self.characterNames.append('pornito')
                    self.characterNames.append('Stigma')
                    self.characterNames.append('PirateAli')
                    self.characterNames.append('LordSmash')
                    self.characterNames.append('Fantastic')
                    self.characterNames.append('DevilKronk')
                    self.characterNames.append('StigMan')
                    bs.screenMessage('Stigma Characters Unlocked',
                        color=(1, 1, 1),
                        clients=[self._player.getInputDevice().getClientID()],
                        transient=True)
            except:
                bs.printException()


def _getName(self, full=True, clamp=False):
    nameRaw = name = self.profileNames[self.profileIndex]
    if name == '_random':
        try:
            inputDevice = self._player.getInputDevice()
        except Exception:
            inputDevice = None
        if inputDevice is not None:
            name = inputDevice._getDefaultPlayerName()
        else:
            name = 'Invalid'
        if not full:
            clamp = True
    elif name == '__account__':
        try:
            inputDevice = self._player.getInputDevice()
        except Exception:
            inputDevice = None
        if inputDevice is not None:
            name = inputDevice._getAccountName(full)
        else:
            name = 'Invalid'
        if not full:
            clamp = True
    elif name == '_edit':
        # FIXME - this causes problems as an Lstr, but its ok to
        # explicitly translate for now since this is only shown on the host.
        name = (bs.Lstr(
            resource='createEditPlayerText',
            fallbackResource='editProfileWindow.titleNewText').evaluate())
    else:
        # if we have a regular profile marked as global with an icon,
        # use it (for full only)
        if full:
            try:
                if self.profiles[nameRaw].get('global', False):
                    icon = (bs.uni(
                        self.profiles[nameRaw]['icon'] if 'icon' in
                        self.profiles[nameRaw] else bs.getSpecialChar('logo')))
                    name = icon + name
            except Exception:
                bs.printException('Error applying global icon')
        else:
            # we now clamp non-full versions of names so there's at
            # least some hope of reading them in-game
            clamp = True

    if clamp:
        # in python < 3.5 some unicode chars can have length 2, so we need
        # to convert to raw int vals for safe trimming
        nameChars = bs.uniToInts(name)
        if len(nameChars) > 10:
            name = bs.uniFromInts(nameChars[:10]) + '...'
    # return 'INVALID NAME' if name.encode('ascii','ignore') == '' else name
    return name
